﻿using HMS.Core.Abstractions;
using HMS.Core.Annotations;
using HMS.MultiTenant.SqlServer;
using HMS.TenantServer;
using HMS.TenantServer.Abstractions;
using HMS.TenantServer.Authorization;
using HMS.TenantServer.GenerateContext;
using HMS.TenantServer.Impl;
using Microsoft.AspNetCore.Authorization;
using Microsoft.EntityFrameworkCore;
using Microsoft.EntityFrameworkCore.Storage;

namespace Microsoft.Extensions.DependencyInjection;

public static class ServiceCollectionExtensions
{
    public static IServiceCollection AddMultiTenantCore(this IServiceCollection services, IConfiguration configuration)
    {
        services.AddSqlServerMultiTenant();

        services.AddDbContext<DbContext, TenantDbContext>(options =>
        {
            options
                .EnableDetailedErrors()
                .UseSqlServer(configuration.GetConnectionString("DataContext"));
        });

        services.AddDbContext<TableGenerateContext>((sp, options) =>
        {
            var modelBuilderProvider = sp.GetRequiredService<IModelBuilderProvider>();
            var modelRuntimeInitializerProvider = sp.GetRequiredService<IModelRuntimeInitializerProvider>();

            var tenant = sp.GetRequiredService<IScopedTenantService>().Tenant;

            if (tenant is not null)
            {
                var modelBuilder = modelBuilderProvider.GetModelBuilder();

                modelBuilder.Model.SetDefaultSchema(tenant.Schema);

                modelBuilder = modelBuilder.ApplyConfigurationsFromAssembly(
                    typeof(TableGenerateContext).Assembly,
                    x => x.IsDefined(typeof(GenerateConfigurationAttribute), true));

                var model = modelRuntimeInitializerProvider
                    .ModelRuntimeInitializer.Initialize(modelBuilder.FinalizeModel());

                options
                    .EnableDetailedErrors()
                    .UseSqlServer(configuration.GetConnectionString("TableContext"))
                    .ReplaceService<IRelationalDatabaseCreator, SqlServerDynamicDatabaseCreator>()
                    .UseModel(model);
            }
        }, ServiceLifetime.Scoped);

        services.AddJwt(configuration);
        services.AddSingleton<IAuthorizationHandler, TenantAuthorizationHandler>();
        services.AddAuthorization(options =>
        {
            options.AddPolicy("Root", policy => policy.Requirements.Add(new TenantAuthorizationRequirement { IsRoot = true }));
            options.AddPolicy("Ultimate", policy => policy.Requirements.Add(new TenantAuthorizationRequirement { License = "Ultimate" }));
        });

        services.AddScoped<ITenantManager, TenantManager>();
        services.AddScoped<IScopedTenantService, ScopedTenantService>();

        return services;
    }

    public static WebApplication Initlialize(this WebApplication app)
    {
        using var scope = app.Services.CreateScope();

        var context = scope.ServiceProvider.GetRequiredService<DbContext>();

        context.Database.EnsureCreated();

        return app;
    }
}